﻿using System;
using System.IO;
using System.Security;

namespace FtpLibDotNet
{
    public class FtpFileRequest : IDisposable
    {
        private FtpConnection connection;
        private Uri address;

        public FtpFileRequest(string address)
            :this(new Uri(address))
        {
        }

        public FtpFileRequest(Uri address)
            :this(address, "anonymous")
        {
        }

        public FtpFileRequest(string address, string username)
            : this(new Uri(address), username)
        {
        }

        public FtpFileRequest(Uri address, string username)
        {
            this.address = address;

            this.connection = new FtpConnection(address.Host);
            this.connection.Open();

            FtpResponse userResponse = this.connection.SendCommand(FtpCommands.USER(username));
            if (userResponse.Code != 331 && userResponse.Code != 230)
            {
                this.connection.Close();
                throw new FtpCommandException(userResponse, "Could not connect to remote server.");
            }
            if (userResponse.Code != 230)
            {
                this.connection.Close();
                throw new FtpCommandException(userResponse, "Could not connect to remote server. Password required.");
            }
        }

        public FtpFileRequest(string address, string username, string password)
            :this(new Uri(address), username, password)
        {
        }

        public FtpFileRequest(Uri address, string username, string password)
            :this(address, username, password.ConvertToSecureString())
        {
        }

        public FtpFileRequest(string address, string username, SecureString password)
            :this(new Uri(address), username, password)
        {
        }

        public FtpFileRequest(Uri address, string username, SecureString password)
        {
            this.address = address;

            this.connection = new FtpConnection(address.Host);
            this.connection.Open();

            FtpResponse userResponse = this.connection.SendCommand(FtpCommands.USER(username));
            if (userResponse.Code != 331 && userResponse.Code != 230)
            {
                this.connection.Close();
                throw new FtpCommandException(userResponse, "Could not connect to remote server. Wrong username.");
            }
            if (userResponse.Code != 230)
            {
                FtpResponse passResponse = this.connection.SendCommand(FtpCommands.PASS(password));
                if (passResponse.Code != 230 && passResponse.Code != 202)
                {
                    this.connection.Close();
                    throw new FtpCommandException(passResponse, "Could not connect to remote server. Wrong username or password.");
                }
            }
        }

        public void DownloadFile(string localFilename)
        {
            FileStream destination = new FileStream(localFilename, FileMode.Create);
            Stream source = this.GetDownloadStream();
            source.CopyTo(destination);
            destination.Close();
            source.Close();
            FtpResponse response = this.connection.ReadResponse();
            if (response.Code != 226 && response.Code != 250)
                throw new FtpCommandException(response, "Could not download a file.");
        }

        public Stream GetDownloadStream()
        {
            this.connection.SetBinaryMode();
            FtpStream stream = new FtpStream(this.connection.CreateDataSocket(), FtpStreamMode.Download);

            FtpResponse response = this.connection.SendCommand(FtpCommands.RETR(this.address.Query));
            if (response.Code != 150 && response.Code != 125 && response.Code != 110)
                throw new FtpCommandException(response, "Could not download a file.");

            return stream;
        }

        void IDisposable.Dispose()
        {
            if (this.connection != null && this.connection.Connected)
                this.connection.Close();
        }
    }
}